
using System;
using System.CodeDom.Compiler;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using VSLangProj;
using IServiceProvider=Microsoft.VisualStudio.OLE.Interop.IServiceProvider;

namespace Mistral.VisualStudio
{
	/// <summary>
	/// Base code generator with site implementation
	/// </summary>
	[ComVisible( true )]
	public abstract class BaseCodeGeneratorWithSite : BaseCodeGenerator, IObjectWithSite
	{
		#region Fields

		private object _site;
		private CodeDomProvider _codeDomProvider;
		private ServiceProvider _serviceProvider;

		#endregion

		#region IObjectWithSite Members

		/// <summary>
		/// GetSite method of IOleObjectWithSite
		/// </summary>
		/// <param __name="riid">interface to get</param>
		/// <param __name="ppvSite">IntPtr in which to stuff return value</param>
		void IObjectWithSite.GetSite(ref Guid riid, out IntPtr ppvSite)
		{
			if (_site == null)
			{
				throw new COMException("object is not sited", VSConstants.E_FAIL);
			}

			IntPtr pUnknownPointer = Marshal.GetIUnknownForObject(_site);
			IntPtr intPointer;
			Marshal.QueryInterface(pUnknownPointer, ref riid, out intPointer);

			if (intPointer == IntPtr.Zero)
			{
				throw new COMException("site does not support requested interface", VSConstants.E_NOINTERFACE);
			}

			ppvSite = intPointer;
		}

		/// <summary>
		/// SetSite method of IOleObjectWithSite
		/// </summary>
		/// <param __name="pUnkSite">site for this object to use</param>
		void IObjectWithSite.SetSite(object pUnkSite)
		{
			_site = pUnkSite;
			_codeDomProvider = null;
			_serviceProvider = null;
		}

		#endregion

		public static string GetPropertyValue( EnvDTE.Properties properties, string name )
		{
			if( properties != null )
			{
				foreach ( Property property in properties )
				{
					if ( String.Compare( property.Name, name, true ) == 0 )
						return property.Value.ToString();
				}				
			}
			return null;
		}

		public static string GenerateHeader( TimeSpan timeSpan )
		{
			StringBuilder sb = new StringBuilder();
			sb.AppendLine( "#region AutoGenerateInfo" );
			sb.AppendLine();
			sb.AppendFormat( "///	<user>{0}</user>", Environment.UserName );
			sb.AppendLine();
			sb.AppendFormat( "///	<date>{0}</date>", DateTime.Now );
			sb.AppendLine();
			sb.AppendFormat( "///	<duration>{0} sec</duration>", timeSpan.TotalSeconds );
			sb.AppendLine();
			sb.AppendLine();
			sb.AppendLine( "#endregion" );
			sb.AppendLine();
			return sb.ToString();
		}

		/// <summary>
		/// Demand-creates a ServiceProvider
		/// </summary>
		private ServiceProvider SiteServiceProvider
		{
			get
			{
				if (_serviceProvider == null)
				{
					_serviceProvider = new ServiceProvider(_site as IServiceProvider);
					Debug.Assert(_serviceProvider != null, "Unable to get ServiceProvider from site object.");
				}
				return _serviceProvider;
			}
		}

		/// <summary>
		/// Method to get a service by its GUID
		/// </summary>
		/// <param __name="serviceGuid">GUID of service to retrieve</param>
		/// <returns>An object that implements the requested service</returns>
		protected object GetService(Guid serviceGuid)
		{
			return SiteServiceProvider.GetService(serviceGuid);
		}

		/// <summary>
		/// Method to get a service by its Type
		/// </summary>
		/// <param __name="serviceType">Type of service to retrieve</param>
		/// <returns>An object that implements the requested service</returns>
		protected object GetService(Type serviceType)
		{
			return SiteServiceProvider.GetService(serviceType);
		}

		/// <summary>
		/// Returns a CodeDomProvider object for the language of the project containing
		/// the project item the generator was called on
		/// </summary>
		/// <returns>A CodeDomProvider object</returns>
		protected virtual CodeDomProvider GetCodeProvider()
		{
			if (_codeDomProvider == null)
			{
				switch (GetProject().CodeModel.Language)
				{
					case CodeModelLanguageConstants.vsCMLanguageCSharp:
						_codeDomProvider = CodeDomProvider.CreateProvider("C#");
						break;
					case CodeModelLanguageConstants.vsCMLanguageVB:
						_codeDomProvider = CodeDomProvider.CreateProvider("VB");
						break;
					case CodeModelLanguageConstants2.vsCMLanguageJSharp:
						_codeDomProvider = CodeDomProvider.CreateProvider("vj#");
						break;
					case CodeModelLanguageConstants.vsCMLanguageMC:
						_codeDomProvider = CodeDomProvider.CreateProvider("mc");
						break;
					default:
						_codeDomProvider = CodeDomProvider.CreateProvider("C#");
						break;
				}
			}
			return _codeDomProvider;
		}

		/// <summary>
		/// Gets the default extension of the output file from the CodeDomProvider
		/// </summary>
		/// <returns></returns>
		protected override string GetDefaultExtension()
		{
			CodeDomProvider codeDom = GetCodeProvider();
			Debug.Assert(codeDom != null, "CodeDomProvider is NULL.");
			string extension = codeDom.FileExtension;
			if (extension != null && extension.Length > 0)
			{
				extension = "." + extension.TrimStart(".".ToCharArray());
			}
			return extension;
		}

		/// <summary>
		/// Returns the EnvDTE.ProjectItem object that corresponds to the project item the code 
		/// generator was called on
		/// </summary>
		/// <returns>The EnvDTE.ProjectItem of the project item the code generator was called on</returns>
		protected ProjectItem GetProjectItem()
		{
			object p = GetService(typeof(ProjectItem));
			Debug.Assert(p != null, "Unable to get Project Item.");
			return (ProjectItem)p;
		}

		/// <summary>
		/// Returns the EnvDTE.Project object of the project containing the project item the code 
		/// generator was called on
		/// </summary>
		/// <returns>
		/// The EnvDTE.Project object of the project containing the project item the code generator was called on
		/// </returns>
		protected Project GetProject()
		{
			return GetProjectItem().ContainingProject;
		}

		/// <summary>
		/// Returns the VSLangProj.VSProjectItem object that corresponds to the project item the code 
		/// generator was called on
		/// </summary>
		/// <returns>The VSLangProj.VSProjectItem of the project item the code generator was called on</returns>
		protected VSProjectItem GetVSProjectItem()
		{
			return (VSProjectItem)GetProjectItem().Object;
		}

		/// <summary>
		/// Returns the VSLangProj.VSProject object of the project containing the project item the code 
		/// generator was called on
		/// </summary>
		/// <returns>
		/// The VSLangProj.VSProject object of the project containing the project item 
		/// the code generator was called on
		/// </returns>
		protected VSProject GetVSProject()
		{
			return (VSProject)GetProject().Object;
		}
	}
}